/*
 * SignatureSliceIndex.h
 *
 *  Created on: Nov 20, 2012
 *      Author: Federico Orquera
 */

#ifndef SIGNATURESLICEINDEX_H_
#define SIGNATURESLICEINDEX_H_

#include <list>
#include <memory>
#include <exception>

#include "fileHandler/FileHandlerInterface.h"

namespace SignatureSlicingIndex {

typedef std::list<KeyWordID> KeyWordList;
typedef std::list<PhraseID> PhraseList;

class PhraseException: public std::exception {
	virtual const char* what() const throw () {
		return "El ID de Frase excede el maximo";
	}
};

class KeyWordException: public std::exception {
	virtual const char* what() const throw () {
		return "El ID de KeyWord excede el maximo";
	}
};

class KeyWordBeginException: public std::exception {
	virtual const char* what() const throw () {
		return "El ID de KeyWord es menor al minimo";
	}
};

template<size_t phrases> class SignatureSliceIndex {

private:

	typedef FileHandlerInterface<phrases> Handler;

	enum SearchOptions{
		AND, OR, NAND, NOR
	};

	void validateKeyWordID(const KeyWordID& key) {
		if (key < handler->begin())
			throw new KeyWordBeginException;
		if (key >= handler->end())
			throw new KeyWordException;

	}
	void validatePhraseID(const PhraseID& phrase) {
		if (phrase >= phrases)
			throw new PhraseException;
	}

public:

	typedef SignatureRegister<phrases, KeyWordID> Slice;
	typedef std::shared_ptr<Slice> Slice_ptr;

	/**
	 * Se crea con un handler que respete la interfaz de File Handler
	 * @param newfileHandler
	 */
	SignatureSliceIndex(Handler* newfileHandler) {
		this->handler = newfileHandler;
	}

	virtual ~SignatureSliceIndex() {
	}

	/**
	 * Permite insertar una lista de keywords para una frase
	 * @param phrase
	 * @param keyWordList
	 */
	virtual void InsertRegister(const KeyWordID& keyWord,
			const PhraseList& phraseList) {

		Slice_ptr newSlice = this->getNewSlice(keyWord);

		for (auto it = phraseList.begin(); it != phraseList.end(); it++){
			this->validatePhraseID(*it);
			(newSlice->getSignature())[(*it)] = true;
		}

		handler->saveSlice(newSlice);
	}

	/**
	 * Permite actualizar el valor de una unica keyword para una frase
	 * @param phrase
	 * @param keyWord
	 * @param value
	 */
	virtual void UpdateRegister(const PhraseID& phrase,
			const KeyWordID& keyWord, const bool value) {
		this->setKeyWord(phrase, keyWord, value);
	}

	/**
	 * Borra la frase del indice de firmas
	 * @param phrase
	 */

	virtual void DeleteRegister(const PhraseID& phrase) {
		for (auto it = handler->begin(); it != handler->end(); it++)
			this->setKeyWord(phrase, it, false);
	}

	/**
	 * Busca las Frases que contienen a keyWord
	 * @param keyWord
	 * @return
	 */

	virtual PhraseList SearchPhrases(const KeyWordID& keyWord) {
		Slice_ptr slice = handler->getSlice(keyWord);
		return this->SearchInSignature(slice->getSignature());
	}

	/**
	 * Busca las frases que contengan por lo menos uno de los terminos de la lista
	 * @param keyWordList
	 * @return
	 */
	virtual PhraseList SearchAnyPhrase(const KeyWordList& keyWordList) {
		return this->MixedSearch(keyWordList, OR);
	}

	/**
	 * Busca las frases que contengan TODOS los elementos de la lista
	 * @param keyWordList
	 * @return
	 */
	virtual PhraseList SearchAllPhrases(const KeyWordList& keyWordList) {
		return this->MixedSearch(keyWordList, AND);
	}

	/**
	 * Busca las frases que NO contengan ALGUNO de los elementos de la lista
	 * @param keyWordList
	 * @return
	 */
	virtual PhraseList SearchNotAnyPhrase(const KeyWordList& keyWordList) {
		return this->MixedSearch(keyWordList, NOR);
	}

	/**
	 * Busca las frases que NO contengan TODOS los elementos de la lista
	 * @param keyWordList
	 * @return
	 */
	virtual PhraseList SearchNotAllPhrases(const KeyWordList& keyWordList) {
		return this->MixedSearch(keyWordList, NAND);
	}

protected:

	Handler* handler;

private:

	Slice_ptr getNewSlice(const KeyWordID& keyWord) {
		return Slice_ptr(new Slice(keyWord));
	}

	void setKeyWord(const PhraseID phrase, const KeyWordID keyWord,
			const bool value = true) {

		this->validateKeyWordID(keyWord);
		this->validatePhraseID(phrase);

		handler->updateSliceBit(keyWord, phrase, value);
	}

	PhraseList SearchInSignature(const typename Slice::Signature signature) {

		PhraseList returnList;

		for (unsigned int it = 0; it < phrases; it++)
			if (signature[it])
				returnList.push_back(it);

		return returnList;

	}

	PhraseList MixedSearch(const KeyWordList& keyWordList,
			const SearchOptions operation ) {

		typename Slice::Signature signatures;
		Slice_ptr slice;

		if (operation == AND)
			signatures.set();

		for (auto it = keyWordList.begin(); it != keyWordList.end(); it++) {
			this->validateKeyWordID(*it);
			slice = handler->getSlice(*it);
			switch (operation) {
			case AND:
			case NAND:
				signatures = signatures & slice->getSignature();
				break;
			case OR:
			case NOR:
				signatures = signatures | slice->getSignature();
				break;
			default:
				//TODO Tirar exception?
				break;

			}
		}

		if (operation == NAND || operation == NOR)
			signatures.flip();

		return this->SearchInSignature(signatures);

	}

};

}

#endif /* SIGNATURESLICEINDEX_H_ */
